#include "tagfs_tagtree.h"
#include <vector>

// -----------------------------------------------------------------------
void TagFS_TagTree::push(std::string newNode, TagFS_TagTree::Mode newMode)
// -----------------------------------------------------------------------
{
	if (newMode == mode)
	{
		tags.push_back(newNode);
		nodeOrder.push(NODE_TAG);
		mode = newMode;
	}
	else
	{
		if (nodeOrder.size() <= 1)
		{
			tags.push_back(newNode);
			nodeOrder.push(NODE_TAG);
			mode = newMode;
		}
		else
		{
			TagFS_TagTree node = *this;
			while (pop());
			push(node);
			push(newNode, newMode);
		}
	}
}

// -------------------------------------------------------------------------
void TagFS_TagTree::push(TagFS_TagTree newNode, TagFS_TagTree::Mode newMode)
// -------------------------------------------------------------------------
{
	if (newMode == mode)
	{
		trees.push_back(newNode);
		nodeOrder.push(NODE_TREE);
		mode = newMode;
	}
	else
	{
		if (nodeOrder.size() <= 1)
		{
			trees.push_back(newNode);
			nodeOrder.push(NODE_TREE);
			mode = newMode;
		}
		else
		{
			TagFS_TagTree node = *this;
			while (pop());
			push(node);
			push(newNode, newMode);
		}
	}
}

// ------------------------
bool TagFS_TagTree::pop()
// ------------------------
{
	if (!nodeOrder.empty())
	{
		if (nodeOrder.top() == NODE_TREE)
		{
			trees.pop_back();
		}
		else if (nodeOrder.top() == NODE_TAG)
		{
			tags.pop_back();
		}
		nodeOrder.pop();
		return true;
	}
	return false;
}

// ----------------------------------------------------
bool TagFS_TagTree::parse(std::list<std::string> tagList)
// ----------------------------------------------------
{
	// create a vector tree and push an empty one
	std::vector<TagFS_TagTree> treeVector;
	TagFS_TagTree emptyTree;
	treeVector.push_back(emptyTree);
	// declate flags and mode stack
	std::string tmp;
	std::stack<TagFS_TagTree::Mode> modeStack;
	bool writeFlag = false;
	enum { PAREN_NONE, PAREN_OPND, PAREN_CLSD } parenFlag = PAREN_NONE;
	uint8_t openedPar = 0;
	uint8_t closedPar = 0;
	// create the tree
	std::list<std::string>::iterator i;
	for (i=tagList.begin(); i!=tagList.end(); ++i)
	{
		tmp = (*i);

		while (!tmp.empty())
		{
			if (tmp[0] == '(' && !writeFlag)
			{
				tmp = tmp.substr(1);
				treeVector.push_back(emptyTree);
				if (parenFlag == PAREN_OPND)
				{
					modeStack.push(TagFS_TagTree::MODE_AND);
				}
				parenFlag = PAREN_OPND;
				++openedPar;
			}
			else if (tmp[0] == ')' && !writeFlag)
			{
				// sanity check
				if (treeVector.size() < 2)
				{
					return false;
				}
				tmp = tmp.substr(1);
				if (parenFlag == PAREN_CLSD)
				{
					treeVector[ treeVector.size()-2 ].push(treeVector.back(), modeStack.top());
					treeVector.pop_back();
					modeStack.pop();
				}
				parenFlag = PAREN_CLSD;
				++closedPar;
			}
			else if (tmp[0] == '|')
			{
				modeStack.push(TagFS_TagTree::MODE_OR);
				writeFlag = true;
				tmp = tmp.substr(1);
			}
			else if (tmp[0] != '(' && tmp[0] != ')' && tmp[0] != '|')
			{
				modeStack.push(TagFS_TagTree::MODE_AND);
				writeFlag = true;
			}
			if (writeFlag)
			{
				treeVector.back().push(tmp, modeStack.top());
				if (parenFlag == PAREN_NONE)
				{
					modeStack.pop();
				}
				else if (parenFlag == PAREN_OPND)
				{
					parenFlag = PAREN_NONE;
				}
				else if (parenFlag == PAREN_CLSD)
				{
					modeStack.pop();
					treeVector[ treeVector.size()-2 ].push(treeVector.back(), modeStack.top());
					treeVector.pop_back();
					modeStack.pop();
					parenFlag = PAREN_NONE;
				}
				tmp.clear();
				writeFlag = false;
			}
		}
	}
	if (closedPar == openedPar)
	{
		*this = treeVector.back();
		return true;
	}
	else
	{
		return false;
	}
}

// ----------------------------------------------------------------------------------------------------------------------------------
std::list<uint64_t> TagFS_TagTree::compute(TagFS_Map* maps)
// ----------------------------------------------------------------------------------------------------------------------------------
{
	std::list<uint64_t> fids;
	std::list<std::list<uint64_t> > tmp_fids;
	std::list<std::list<uint64_t> >::iterator x;
	std::list<uint64_t>::iterator a,b;
	std::list<std::string>::iterator ti;
	std::list<TagFS_TagTree>::iterator tti;

	// empty trees = untagged
	if (tags.size() == 0 && trees.size() == 0)
	{
		return maps->tagFIDs("");
	}

	for (ti=tags.begin(); ti!=tags.end(); ++ti)
	{
		// get FIDs for each tag
		fids.clear();
		if (*ti == "*")
		{
			fids = maps->tagFIDs();
			tmp_fids.push_back(fids);
		}
		else if ((*ti)[0] == '~')
		{
			fids = maps->tagFIDs();
			std::list<uint64_t> fids2 = maps->tagFIDs((*ti).substr(1));
			for (a=fids2.begin(); a!=fids2.end(); ++a)
			{
				fids.remove(*a);
			}
			tmp_fids.push_back(fids);
		}
		else if ((*ti)[0] == '!')
		{
			tmp_fids.push_back(fids);
		}
		else
		{
			if ((*ti)[0] == '&')
			{
				fids = maps->tagFIDs((*ti).substr(1));
			}
			else
			{
				fids = maps->tagFIDs(*ti);
			}
			tmp_fids.push_back(fids);
		}
	}

	for (tti=trees.begin(); tti!=trees.end(); ++tti)
	{
		// get FIDs for each tree (recursive)
		tmp_fids.push_back(tti->compute(maps));
	}
	
	if (tmp_fids.size() > 1)
	{
		if (mode == MODE_AND)
		{
			// delete non-matching
			x = tmp_fids.begin();
			fids = *x;
			++x;
			for (x; x!=tmp_fids.end(); ++x)
			{
				for (a=fids.begin(); a!=fids.end(); ++a)
				{
					if (!x->empty())
					{
						for (b=x->begin(); b!=x->end(); ++b)
						{
							if ( (*a) != (*b) )
							{
								fids.erase(a);
								x->erase(b);
								--a;
								break;
							}
						}
					}
					else
					{
						fids.clear();
						return fids;
					}
				}
			}
		}
		else if (mode == MODE_OR)
		{
			// merge
			for (x=tmp_fids.begin(); x!=tmp_fids.end(); ++x)
			{
				fids.merge((*x));
			}
			fids.sort();
			fids.unique();
		}
	}
	else
	{
		fids = tmp_fids.front();
	}

	return fids;
}

/*

// ------------------
std::string TagFS_TagTree::string()
// ------------------
{
	std::string str;
	std::string tag;
	TagTree tree;
	std::list<std::string>::iterator ti;
	std::list<TagTree>::iterator tti;

	// process tags
	for (ti=tags.begin(); ti!=tags.end(); ++ti)
	{
		if (str.size() > 0)
		{
			if (mode == MODE_AND)
			{
				str.append(" && ");
			}
			else if (mode == MODE_OR)
			{
				str.append(" || ");
			}
		}
		tag = *ti;
		if (tag[0] == '~')
		{
			tag = "~ " + tag.substr(1);
		}
		else if (tag[0] == '&')
		{
			tag = tag.substr(1);
		}
		str.append(tag);
	}

	// process trees
	for (tti=trees.begin(); tti!=trees.end(); ++tti)
	{
		if (str.size() > 0)
		{
			if (mode == MODE_AND)
			{
				str.append(" && ");
			}
			else if (mode == MODE_OR)
			{
				str.append(" || ");
			}
		}
		tree = *tti;
		str.append("( ");
		str.append(tree.string());
		str.append(" )");
	}

	return str;
}

*/
